/* fifo 的一个潜在的条件是 ： 写指针总大于或等于读指针*/
/**
 * @note : 异步FIFO，读写时钟不一致。
 * 
 * 
 * 
 */
module top #(
	parameter ADDR_WIDTH = 8  ,
	parameter DATA_WIDTH = 16 ,
	parameter FIFO_DEPTH = 256 
	)(
	input  wire 					wr_clk ,     	// write Clock
	input  wire 					wr_en  ,
	input  wire 					rd_clk ,		// read  Clock
	input  wire 					rd_en  ,
	input  wire 					rst_n ,
	input  wire [DATA_WIDTH-1:0 ] 	data_in ,
	output logic					vaild  	, //output data  valid .
	output logic [DATA_WIDTH-1:0]   data_o 	,
	output logic					empty  	,
	output logic					full   
	
);

	logic [ADDR_WIDTH - 1 : 0 ] wr_addr_ptr ; //write address pointer 
	logic [ADDR_WIDTH - 1 : 0 ] rd_addr_ptr ; //read address  pointer

	logic [ADDR_WIDTH - 1 : 0 ] wr_addr 	;	//address
	logic [ADDR_WIDTH - 1 : 0 ] rd_addr 	;	//address

	/**************gray code ****使用格雷码来减小亚稳态***********************/
	logic [ADDR_WIDTH - 1 : 0 ] wr_addr_gray	;
	logic [ADDR_WIDTH - 1 : 0 ] wr_addr_gray_d1	;//delay 1 clk 
	logic [ADDR_WIDTH - 1 : 0 ] wr_addr_gray_d2	;//delay 2 clk 

	logic [ADDR_WIDTH - 1 : 0 ] rd_addr_gray	;
	logic [ADDR_WIDTH - 1 : 0 ] rd_addr_gray_d1	;//delay 1 clk 
	logic [ADDR_WIDTH - 1 : 0 ] rd_addr_gray_d2	;//delay 2 clk 


	logic [DATA_WIDTH-1:0] fifo_ram [0:FIFO_DEPTH-1]; //fifo ram 

	//=================write fifo================================


	always_ff @(posedge wr_clk or negedge rst_n) begin : write_func
		if(~rst_n)begin	
			for(int wr_i = 0 ; wr_i < FIFO_DEPTH ; wr_i ++)begin : init
				fifo_ram[wr_i] <= {DATA_WIDTH{1'b0}};
			end
		end else begin
			if(wr_en && (~full))begin // write enable and no full 
				fifo_ram[wr_addr] <= data_in ; 
			end else begin
				fifo_ram[wr_addr] <= fifo_ram[wr_addr] ; //keeping data .
			end
		end
	end
		

	//=================read fifo =======================================
	always_ff @(posedge rd_clk or negedge rst_n) begin : read_func
		if(~rst_n) begin
			data_o <= {DATA_WIDTH{1'b1}};
			vaild  <= 1'd0 ;
		end else begin
			if(rd_en && (~empty)) begin
				data_o <= fifo_ram[rd_addr] ;
				vaild  <= 1'b1 ;
			end else begin
				data_o <= {DATA_WIDTH{1'b1}} ;
				vaild <= 1'b0 ;
			end
		end 
	end
	assign wr_addr = wr_addr_ptr[(ADDR_WIDTH-1)-:ADDR_WIDTH] ; // [x-:d] 表示从x位开始递减，位宽为d
	assign rd_addr = rd_addr_ptr[(ADDR_WIDTH-1)-:ADDR_WIDTH] ; // [x+:d] 表示从x位开始递增，位宽为d

	//===========gray code 同步===========保证跨时钟域正确
	always_ff @( posedge wr_clk) begin : rd_gray
		rd_addr_gray_d1 <= rd_addr_gray 	;
		rd_addr_gray_d2 <= rd_addr_gray_d1 	;
	end
	always_ff @(posedge rd_clk)  begin : wr_gray 
		wr_addr_gray_d1 <= wr_addr_gray ;
		wr_addr_gray_d2 <= wr_addr_gray_d1 ;
	end


	//=========write pointer ==================================
	always_ff @(posedge wr_clk or negedge rst_n) begin
		if(~rst_n)begin
			wr_addr_ptr <= 'h0 ;
		end else begin
			if(wr_en && (~full))begin
				wr_addr_ptr <= wr_addr_ptr + 1'b1 ; 
			end else begin
				wr_addr_ptr <= wr_addr_ptr ;
			end
		end
	end

	//=========read  pointer ==================================
	always_ff @(posedge rd_clk or negedge rst_n) begin
		if(~rst_n)begin
			rd_addr_ptr <= 'h0 ;
		end else begin
			if(rd_en && (~empty))begin
				rd_addr_ptr <= rd_addr_ptr + 1'b1 ; 
			end else begin
				rd_addr_ptr <= rd_addr_ptr ;
			end
		end
	end

	//=======gray code translation
	assign wr_addr_gray = (wr_addr_ptr >> 1 ) ^ wr_addr_ptr ;
	assign rd_addr_gray = (rd_addr_ptr >> 1 ) ^ rd_addr_ptr ;

	//======full and emmpty ======================
	assign full  = (wr_addr_gray == {~(rd_addr_gray_d2[ADDR_WIDTH-1-:2]),rd_addr_gray_d2[ADDR_WIDTH-2:0]});//高2位不同
	assign empty = ( rd_addr_gray == wr_addr_gray_d2 ) ; //当读的指针等于写的指针时，就判断为空


endmodule : top